#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <gdiplus.h>
#include "twimageconv.h"

#pragma comment(lib,"gdiplus.lib")

int LoadFileToMemory2( const char* pszFilename, BYTE** ppBuffer )
{
	FILE* fp = fopen( pszFilename, "rb" );
	if( fp == 0 )
		return false;
	fseek ( fp, 0, SEEK_END );
	DWORD dwFileSize = ftell( fp );
	if( dwFileSize == 0 )
	{
		fclose( fp );
		return 0;
	}
	BYTE* pBuffer = new BYTE[dwFileSize];
	rewind( fp );
	fread( pBuffer, dwFileSize, 1, fp );
	fclose( fp );
	*ppBuffer = pBuffer;
	return dwFileSize;
}

BOOL Buffer_SaveRGBToJpg(	BYTE* pBitmap,
					DWORD dwWidth,
					DWORD dwHeight,
					char* pszJpgFilename,
					int nQuality );

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
	UINT  num = 0;          // number of image encoders
	UINT  size = 0;         // size of the image encoder array in bytes

	Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;

	Gdiplus::GetImageEncodersSize(&num, &size);
	if(size == 0)
		return -1;  // Failure

	pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
	if(pImageCodecInfo == NULL)
		return -1;  // Failure

	Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);

	for(UINT j = 0; j < num; ++j)
	{
		if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
		{
			*pClsid = pImageCodecInfo[j].Clsid;
			free(pImageCodecInfo);
			return j;  // Success
		}    
	}

	free(pImageCodecInfo);
	return -1;  // Failure
}

BOOL ConvertBmpToJpg(	WCHAR* pszBmpFilename, 
						WCHAR* pszJpgFilename,
						int nQuality )
{
	// Initialize GDI+.
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	ULONG_PTR gdiplusToken;
	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

	CLSID             encoderClsid;
	Gdiplus::EncoderParameters encoderParameters;
	ULONG             quality;
	Gdiplus::Status            stat;

	// Get an image from the disk.
	Gdiplus::Image* image = new Gdiplus::Image( pszBmpFilename );
	//assert( image != NULL && "cannot open bmp" );
	if( !image )
	{
		assert( false );
		return FALSE;
	}

	// Get the CLSID of the JPEG encoder.
	GetEncoderClsid(L"image/jpeg", &encoderClsid);

	// Before we call Image::Save, we must initialize an
	// EncoderParameters object. The EncoderParameters object
	// has an array of EncoderParameter objects. In this
	// case, there is only one EncoderParameter object in the array.
	// The one EncoderParameter object has an array of values.
	// In this case, there is only one value (of type ULONG)
	// in the array. We will let this value vary from 0 to 100.

	encoderParameters.Count = 1;
	encoderParameters.Parameter[0].Guid = Gdiplus::EncoderQuality;
	encoderParameters.Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
	encoderParameters.Parameter[0].NumberOfValues = 1;

	// Save the image as a JPEG with quality level 0.
	quality = nQuality;
	encoderParameters.Parameter[0].Value = &quality;
	stat = image->Save( pszJpgFilename, &encoderClsid, &encoderParameters);

	BOOL bOK = TRUE;
	if(stat == Gdiplus::Ok)
	{
		//MessageBox( NULL, "save ok", "", MB_OK );
		//wprintf(L"%s saved successfully.\n", L"Shapes001.jpg");
	}
	else
	{
		//MessageBox( NULL, "save failed", "", MB_OK );
		//wprintf(L"%d  Attempt to save %s failed.\n", stat, L"Shapes001.jpg");
		bOK = FALSE;
	}

	delete image;
	Gdiplus::GdiplusShutdown(gdiplusToken);
	return bOK;
}
long Buffer_SaveRGBToBmp(char *filename,long sx,long sy,long xl,long yl,BYTE *bitmap)
{
	int   i,j;
	long offset;
	long img_xl,img_yl;
	BITMAPFILEHEADER bmpfilehdr;
	BITMAPINFOHEADER bmpinfohdr;
	FILE  *fp;
	BYTE  *line_buffer;


	img_xl=xl-sx;
	img_yl=yl-sy;
	bmpfilehdr.bfType=0x4D42; //"BM"
	bmpfilehdr.bfSize=img_xl*img_yl*3+sizeof(bmpfilehdr)+sizeof(bmpinfohdr);
	bmpfilehdr.bfReserved1=0;
	bmpfilehdr.bfReserved2=0;
	bmpfilehdr.bfOffBits=0x36;


	bmpinfohdr.biSize=sizeof(BITMAPINFOHEADER);
	bmpinfohdr.biWidth=img_xl;
	bmpinfohdr.biHeight=img_yl;
	bmpinfohdr.biPlanes=1;
	bmpinfohdr.biBitCount=24;
	bmpinfohdr.biCompression=0;
	bmpinfohdr.biSizeImage=img_xl*img_yl*3;
	bmpinfohdr.biXPelsPerMeter=0x0B12;
	bmpinfohdr.biYPelsPerMeter=0x0B12;
	bmpinfohdr.biClrUsed=0;
	bmpinfohdr.biClrImportant=0;

	line_buffer=new BYTE[img_xl*3+10];
	if(line_buffer==NULL)
		return false;

	fp=fopen((char *)filename,"wb");
	if(fp==NULL)
	{
		return false;
	}
	fwrite(&bmpfilehdr,sizeof(bmpfilehdr),1,fp);
	fwrite(&bmpinfohdr,sizeof(bmpinfohdr),1,fp);


	for(i=yl-1;i>=sy;i--)
	{
		offset=i*xl+sx; 
		for(j=0;j<img_xl;j++)
		{

			line_buffer[j*3+2]=bitmap[(offset+j)*3];
			line_buffer[j*3+1]=bitmap[(offset+j)*3+1];
			line_buffer[j*3]=bitmap[(offset+j)*3+2];

		}
		fwrite(line_buffer,3,img_xl,fp);
	}

	fclose(fp);
	if(line_buffer!=NULL)
		delete line_buffer;

	return true;
}

BOOL Buffer_SaveRGBToJpg(	BYTE* pBitmap,
					DWORD dwWidth,
					DWORD dwHeight,
					char* pszJpgFilename,
					int nQuality )
{
	assert( pBitmap );
	assert( dwWidth > 0 );
	assert( dwHeight > 0 );
	assert( pszJpgFilename );
	assert( nQuality > 0 && nQuality <= 100 );
	if( !Buffer_SaveRGBToBmp( "tmp.bmp", 0, 0, dwWidth, dwHeight, pBitmap ) )
	{
		assert( false && "save rgb to jpg error" );
		return FALSE;
	}
	WCHAR s[256];
	MultiByteToWideChar(CP_ACP, 0, pszJpgFilename, -1, s, 256); 
	BOOL bOK = ConvertBmpToJpg( L"tmp.bmp", s, nQuality );
	remove( "tmp.bmp" );
	return bOK;
}



BYTE* Buffer_ConvertARGBToRGB( DWORD* pdwARGB, DWORD dwWidth, DWORD dwHeight )
{
	assert( dwWidth != 0 && dwHeight != 0 && "invalid size" );
	BYTE* pBitmap = new BYTE[dwWidth*dwHeight*3];
	BYTE*	dst = pBitmap;
	BYTE*	src = (BYTE*)pdwARGB;

	for( int y = 0; y < dwHeight; y++ )
	{
		for( int x = 0; x < dwWidth; x++ )
		{
			BYTE b = src[x*4+0];
			BYTE g = src[x*4+1];
			BYTE r = src[x*4+2];
			*dst++ = r;
			*dst++ = g;
			*dst++ = b;
		}
		src += dwWidth*sizeof( DWORD );
	}
	return pBitmap;
}
BYTE* Buffer_ConvertARGBToAlpha3( DWORD* pdwARGB, DWORD dwWidth, DWORD dwHeight )
{
	assert( dwWidth != 0 && dwHeight != 0 && "invalid size" );
	BYTE* pBitmap = new BYTE[dwWidth*dwHeight*3];
	BYTE*	dst = pBitmap;
	BYTE*	src = (BYTE*)pdwARGB;

	for( int y = 0; y < dwHeight; y++ )
	{
		for( int x = 0; x < dwWidth; x++ )
		{
			BYTE b = src[x*4+3];
			BYTE g = src[x*4+3];
			BYTE r = src[x*4+3];
			*dst++ = r;
			*dst++ = g;
			*dst++ = b;
		}
		src += dwWidth*sizeof( DWORD );
	}
	return pBitmap;
}

DWORD Buffer_ConvertRGBToJpg( BYTE* pRGB, DWORD dwWidth, DWORD dwHeight, int nQuality, BYTE** ppJpgBuffer )
{
	BOOL bOK = Buffer_SaveRGBToJpg( pRGB, dwWidth, dwHeight, "tmp.jpg", nQuality );
	assert( bOK && "save argb to rgb failed" );
	BYTE* pbyJpgBuffer = NULL;
	int nJpgBufferSize = LoadFileToMemory2( "tmp.jpg", &pbyJpgBuffer );
	assert( nJpgBufferSize > 0 && "load jpg file failed" );
	remove( "tmp.jpg" );
	
	*ppJpgBuffer = pbyJpgBuffer;
	return nJpgBufferSize;
}